﻿using System;
using System.Collections.Generic;
using NHibernate.Criterion;
using System.Linq.Expressions;

namespace NHibernate.Sidekick.Data.NHibernate
{
    /// <summary>
    /// Provides an abstract interface for the specified type.
    /// </summary>
    /// <typeparam name="T">The specified type.</typeparam>
    /// <typeparam name="TId">The type of the id.</typeparam>
    public interface IDao<T, TId>
    {
        /// <summary>
        /// Returns the persistent instance with the specified identity.
        /// </summary>
        /// <param name="id">The instance's identifier.</param>
        /// <param name="shouldLock">If set to <c>true</c>, lock the instace.</param>
        /// <returns>The persistent instance, or null if there is no such persistent instance.</returns>
        T GetById(TId id, bool shouldLock);

        /// <summary>
        /// Returns the persistent instance with the specified identity.
        /// </summary>
        /// <param name="id">The instance's identifier.</param>
        /// <returns>The persistent instance, or null if there is no such persistent instance.</returns>
        T GetById(TId id);

        /// <summary>
        /// Loads every instance of the requested type using the supplied <see cref="ICriterion"/>.
        /// If no <see cref="ICriterion"/> is supplied, this behaves like <see cref="GetAll"/>.
        /// </summary>
        /// <returns>Every entity available for the requested type.</returns>
        List<T> GetAll();

        /// <summary>
        /// Loads every instance of the requested type using the supplied <see cref="ICriterion"/>.
        /// If no <see cref="ICriterion"/> is supplied, this behaves like <see cref="GetAll"/>.
        /// </summary>
        /// <param name="maxNumberOfResults">The maxinum number of results.</param>
        /// <param name="firstResult">Accept results following this one.</param>
        /// <param name="expressions">The filtering expressions.</param>
        /// <returns>The collection of entities.</returns>
        List<T> GetByCriteria(int? maxNumberOfResults, int? firstResult, params Expression<Func<T, bool>>[] expressions);

        /// <summary>
        /// Loads a unique instance of the requested type using the supplied <see cref="ICriterion"/>.
        /// If no <see cref="ICriterion"/> is supplied, this behaves like <see cref="GetAll"/>.
        /// </summary>
        /// <param name="expressions">The expressions.</param>
        /// <returns>The unique instance.</returns>
        T GetUniqueByExpression(params Expression<Func<T, bool>>[] expressions);

        /// <summary>
        /// Operates the collection of entities equally.
        /// </summary>
        /// <param name="dataOperation">The data operation.</param>
        /// <param name="entities">The entities.</param>
        void OperateCollectionEqually(DataOperation dataOperation, params T[] entities);

        /// <summary>
        /// Operates the collection of entities differently.
        /// </summary>
        /// <param name="entityDictionary">The entity dictionary.</param>
        void OperateCollectionDifferently(Dictionary<T, DataOperation> entityDictionary);

        /// <summary>
        /// Persist the given transient instance, first assigning a generated identifier.
        /// </summary>
        /// <param name="entity">The transient entity.</param>
        /// <returns>The saved instance.</returns>
        T Save(T entity);

        /// <summary>
        /// Update the persistent instance with the identifier of the given detached instance.
        /// </summary>
        /// <param name="entity">The transient entity.</param>
        /// <returns>The updated instance.</returns>
        T Update(T entity);

        /// <summary>
        /// Either <see cref="Save(T)"/> or <see cref="Update(T)"/> the given instance, depending upon resolution of the unsaved-value checks
        /// </summary>
        /// <param name="entity">A transient or detached instance containing new or updated state .</param>
        /// <returns></returns>
        T SaveOrUpdate(T entity);

        /// <summary>
        /// Remove a persistent instance from the datastore. The argument may be an instance associated
        /// with the receiving Session or a transient instance with an identifier associated with existing
        /// persistent state. This operation cascades to associated instances if the association is mapped
        /// with cascade="delete". 
        /// </summary>
        /// <param name="entity">The instance to be removed.</param>
        void Delete(T entity);

        /// <summary>
        /// Remove a persistent instance from the datastore. The argument may be an instance associated
        /// with the receiving Session or a transient instance with an identifier associated with existing
        /// persistent state. This operation cascades to associated instances if the association is mapped
        /// with cascade="delete". 
        /// </summary>
        /// <param name="id">The instance's identifier.</param>
        void Delete(TId id);

        /// <summary>
        /// Flush the associated ISession and end the unit of work.
        /// </summary>
        void CommitChanges();
        /// <summary>
        /// Forces the underlying <see cref="ITransaction"/> to roll back.
        /// </summary>
        void RollBackChanges();
        /// <summary>
        /// Re-read the state of the given instance from the underlying database.
        /// </summary>
        /// <param name="entity">The persistent instance.</param>
        void Refresh(T entity);
    }
}
